package org.ws.httphelper.model.config;

import com.google.common.collect.Maps;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.ws.httphelper.common.Constant;
import org.ws.httphelper.core.pipeline.HttpPipeline;
import org.ws.httphelper.model.field.ParamField;
import org.ws.httphelper.model.field.ParseField;
import org.ws.httphelper.model.http.ContentType;
import org.ws.httphelper.model.http.HttpMethod;
import org.ws.httphelper.model.http.ResponseType;
import org.ws.httphelper.support.pipeline.DefaultHttpPipeline;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * 一个请求模版.
 * 可针对不同场景创建相应模板.
 * 包含一组相同的操作.
 * 相同的参数类型，请求前置处理，请求后置处理（相同的解析，存储操作。）
 * 针对一个接口，一类相同操作.
 */
public class RequestConfig {
    // 请求方法
    private HttpMethod method = HttpMethod.GET;
    // 默认的header
    private Map<String, String> defaultHeaders = Maps.newHashMap();
    // 默认的cookies：若header同时存在cookie，覆盖header中的
    private Map<String, String> defaultCookers = Maps.newHashMap();
    // 默认的参数
    private Map<String, Object> defaultParams = Maps.newHashMap();
    // 处理pipeline
    private HttpPipeline httpPipeline = new DefaultHttpPipeline();
    // 参数定义
    private Map<String, ParamField> paramFields = Maps.newHashMap();
    // 根目录：可通过相对路径请求，同时若使用绝对路径请求，优先使用传入参数
    private String rootUrl;
    // 请求编码
    private String charset = Constant.UTF_8;
    // 请求内容类型，与Body相结合
    private ContentType contentType;
    // 响应的类型：能够自动解析，根据相应的内容类型执行相应的解析处理
    private ResponseType responseType;
    // 解析的字段定义
    private Map<String, ParseField> parseHtmlFields = Maps.newHashMap();

    public RequestConfig() {
    }

    public RequestConfig(RequestConfig parent) {
        mergeAll(parent);
    }

    public void mergeAll(RequestConfig config){
        Field[] declaredFields = this.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            try {
                String fieldName = field.getName();
                Object o = field.get(config);
                if(StringUtils.equals("rootUrl",fieldName)){
                    String thisUrl = this.rootUrl;
                    String path = (String)o;
                    // 当前为url，需合并的为path
                    if(StringUtils.isNotBlank(path) && StringUtils.isNotBlank(thisUrl)
                        && Pattern.matches(Constant.URL_PATTERN,thisUrl) && path.startsWith("/")){
                        field.set(this, thisUrl+path);
                    }
                    else if(StringUtils.isNotBlank(path)){
                        field.set(this,path);
                    }
                }
                else if (o != null && !(o instanceof Map) && !(o instanceof HttpPipeline)) {
                    if(o instanceof String) {
                        if(StringUtils.isNotBlank((String)o)) {
                            field.set(this, o);
                        }
                    }
                    else {
                        field.set(this, o);
                    }
                }
            }
            catch (Exception e){
                throw new RuntimeException(e.getMessage(),e);
            }
        }
        if(MapUtils.isNotEmpty(config.defaultCookers)) {
            this.defaultCookers.putAll(config.defaultCookers);
        }
        if(MapUtils.isNotEmpty(config.defaultHeaders)) {
            this.defaultHeaders.putAll(config.defaultHeaders);
        }
        if(MapUtils.isNotEmpty(config.defaultParams)) {
            this.defaultParams.putAll(config.defaultParams);
        }
        if(MapUtils.isNotEmpty(config.paramFields)) {
            this.paramFields.putAll(config.paramFields);
        }
        if(MapUtils.isNotEmpty(config.parseHtmlFields)) {
            this.parseHtmlFields.putAll(config.parseHtmlFields);
        }
        ((DefaultHttpPipeline)this.httpPipeline).copyOf((DefaultHttpPipeline)config.getHttpPipeline());
    }

    public HttpMethod getMethod() {
        return method;
    }

    public void setMethod(HttpMethod method) {
        this.method = method;
    }

    public Map<String, String> getDefaultHeaders() {
        return defaultHeaders;
    }

    public void addDefaultHeader(String name, String value) {
        this.defaultHeaders.put(name,value);
    }

    public Map<String, String> getDefaultCookers() {
        return defaultCookers;
    }

    public void addDefaultCookie(String name, String value) {
        this.defaultCookers.put(name,value);
    }

    public HttpPipeline getHttpPipeline() {
        return httpPipeline;
    }

    public void addParamField(ParamField field){
        this.paramFields.put(field.getFieldName(),field);
    }

    public Map<String, ParamField> getParamFields() {
        return paramFields;
    }

    public Map<String, Object> getDefaultParams() {
        return defaultParams;
    }

    public void addDefaultParams(String name, Object value) {
        this.defaultParams.put(name,value);
    }

    public String getRootUrl() {
        return rootUrl;
    }

    public void setRootUrl(String rootUrl) {
        this.rootUrl = rootUrl;
    }

    public String getCharset() {
        return charset;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public ContentType getContentType() {
        return contentType;
    }

    public void setContentType(ContentType contentType) {
        this.contentType = contentType;
    }

    public ResponseType getResponseType() {
        return responseType;
    }

    public void setResponseType(ResponseType responseType) {
        this.responseType = responseType;
    }

    public Map<String, ParseField> getParseHtmlFields() {
        return parseHtmlFields;
    }

    public void addParseHtmlField(ParseField field){
        this.parseHtmlFields.put(field.getFieldName(),field);
    }

    public void setDefaultHeaders(Map<String, String> defaultHeaders) {
        this.defaultHeaders.putAll(defaultHeaders);
    }

    public void setDefaultCookers(Map<String, String> defaultCookers) {
        this.defaultCookers.putAll(defaultCookers);
    }

    public void setDefaultParams(Map<String, Object> defaultParams) {
        this.defaultParams.putAll(defaultParams);
    }

    public void setParamFields(Map<String, ParamField> paramFields) {
        this.paramFields.putAll(paramFields);
    }

    public void setParseHtmlFields(Map<String, ParseField> parseHtmlFields) {
        this.parseHtmlFields.putAll(parseHtmlFields);
    }
}
